package cn.edu.wfit.modules.directionmanagement.util;

import cn.edu.wfit.modules.directionmanagement.config.OnlineIdeConfig;

import java.io.*;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * 代码执行工具类
 */
public class CodeExecutionUtil {
    
    // 线程池用于读取进程输出
    private static final ExecutorService OUTPUT_READER_POOL = Executors.newFixedThreadPool(10);

    /**
     * 执行命令
     *
     * @param command     命令数组
     * @param workingDir  工作目录
     * @param input       输入数据
     * @param config      配置类
     * @return 执行结果
     * @throws Exception 执行异常
     */
    public static ProcessResult executeCommand(String[] command, File workingDir, String input, OnlineIdeConfig config) throws Exception {
        return executeCommand(command, workingDir, input, config, false);
    }

    /**
     * 执行命令（支持Python特殊处理）
     *
     * @param command     命令数组
     * @param workingDir  工作目录
     * @param input       输入数据
     * @param config      配置类
     * @param isPython    是否为Python执行
     * @return 执行结果
     * @throws Exception 执行异常
     */
    public static ProcessResult executeCommand(String[] command, File workingDir, String input, OnlineIdeConfig config, boolean isPython) throws Exception {
        // 安全检查：验证命令是否在白名单中
        if (!isCommandAllowed(command)) {
            return new ProcessResult(false, "", "不允许执行的命令: " + String.join(" ", command));
        }
        
        // 安全检查：验证工作目录是否安全
        if (!isWorkingDirectorySafe(workingDir)) {
            return new ProcessResult(false, "", "工作目录不安全: " + workingDir.getAbsolutePath());
        }
        
        ProcessBuilder processBuilder = new ProcessBuilder(command);
        processBuilder.directory(workingDir);
        processBuilder.redirectErrorStream(true);
        
        // 清理环境变量，减少系统资源访问风险
        Map<String, String> env = processBuilder.environment();
        // 移除可能危险的环境变量
        env.remove("PATH");
        env.remove("JAVA_HOME");
        env.remove("PYTHONPATH");
        env.remove("CLASSPATH");
        env.remove("LD_LIBRARY_PATH");
        env.remove("SHELL");
        env.remove("USER");
        env.remove("HOME");
        
        // 设置安全的环境变量
        String tempDir = System.getProperty("java.io.tmpdir");
        env.put("TEMP", tempDir);
        env.put("TMP", tempDir);
        env.put("TMPDIR", tempDir);
        
        if (System.getProperty("os.name").toLowerCase().contains("win")) {
            // 只为Python设置编码环境变量，Java使用默认编码避免输出提示信息
            if (isPython) {
                // Python在Windows上强制使用GBK编码进行IO，确保中文正确显示
                env.put("PYTHONIOENCODING", "gbk");
            }
        } else {
            if (isPython) {
                env.put("PYTHONIOENCODING", "utf-8");
            }
        }

        Process process = processBuilder.start();

        // 如果有输入数据，写入到进程
        if (input != null && !input.isEmpty()) {
            // 在Windows上使用GBK编码，Linux/Mac上使用UTF-8
            String encoding = System.getProperty("os.name").toLowerCase().contains("win") ? "GBK" : "UTF-8";
            try (OutputStreamWriter writer = new OutputStreamWriter(process.getOutputStream(), encoding)) {
                writer.write(input);
                writer.flush();
            }
        } else {
            // 关闭输出流，表示没有更多输入
            process.getOutputStream().close();
        }

        // 使用线程池管理进程输出读取，提高稳定性
        Future<String> outputFuture = OUTPUT_READER_POOL.submit(() -> {
            return isPython ? readPythonProcessOutput(process.getInputStream()) : readProcessOutput(process.getInputStream());
        });

        // 等待进程执行完成或超时
        boolean finished = process.waitFor(config.getExecutionTimeout(), TimeUnit.SECONDS);

        if (!finished) {
            process.destroyForcibly();
            return new ProcessResult(false, "", "执行超时");
        }

        // 获取输出结果
        String output = outputFuture.get(5, TimeUnit.SECONDS); // 等待最多5秒获取输出

        return new ProcessResult(true, output, "");
    }

    /**
     * 写入文件
     */
    public static void writeToFile(String filePath, String content) throws IOException {
        // 对于Python文件，始终使用UTF-8编码
        // 对于其他文件，在Windows上使用GBK编码，Linux/Mac上使用UTF-8
        String encoding;
        if (filePath.endsWith(".py")) {
            encoding = "UTF-8";
        } else {
            encoding = System.getProperty("os.name").toLowerCase().contains("win") ? "GBK" : "UTF-8";
        }
        try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(filePath), encoding)) {
            writer.write(content);
        }
    }

    /**
     * 读取进程输出
     */
    private static String readProcessOutput(InputStream inputStream) throws IOException {
        StringBuilder output = new StringBuilder();
        // 在Windows上使用GBK编码，Linux/Mac上使用UTF-8
        String encoding = System.getProperty("os.name").toLowerCase().contains("win") ? "GBK" : "UTF-8";
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, encoding))) {
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line).append("\n");
            }
        }
        return output.toString();
    }

    /**
     * 读取Python进程输出（特殊处理）
     */
    private static String readPythonProcessOutput(InputStream inputStream) throws IOException {
        StringBuilder output = new StringBuilder();
        // Python在Windows上输出GBK编码，Linux/Mac上输出UTF-8编码
        String encoding = System.getProperty("os.name").toLowerCase().contains("win") ? "GBK" : "UTF-8";
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, encoding))) {
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line).append("\n");
            }
        }
        return output.toString();
    }

    /**
     * 创建临时目录
     */
    public static String createTempDirectory() throws IOException {
        String tempDir = System.getProperty("java.io.tmpdir") + File.separator + "online_ide_" + UUID.randomUUID().toString();
        File dir = new File(tempDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return tempDir;
    }

    /**
     * 删除目录及其内容
     */
    public static void deleteDirectory(File directory) {
        if (directory.exists()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        deleteDirectory(file);
                    } else {
                        file.delete();
                    }
                }
            }
            directory.delete();
        }
    }

    /**
     * 检查命令是否被允许执行
     * @param command 命令数组
     * @return 是否允许
     */
    private static boolean isCommandAllowed(String[] command) {
        if (command == null || command.length == 0) {
            return false;
        }

        // 使用安全工具类检查命令是否在白名单中
        return CodeSecurityUtil.isCommandAllowed(command);
    }

    /**
     * 检查工作目录是否安全
     * @param workingDir 工作目录
     * @return 是否安全
     */
    private static boolean isWorkingDirectorySafe(File workingDir) {
        if (workingDir == null) {
            return false;
        }

        try {
            // 获取系统临时目录
            String tempDir = System.getProperty("java.io.tmpdir");

            // 检查工作目录是否在临时目录下
            String workingDirPath = workingDir.getCanonicalPath();
            String tempDirPath = new File(tempDir).getCanonicalPath();

            // 工作目录必须在临时目录下
            if (!workingDirPath.startsWith(tempDirPath)) {
                return false;
            }

            // 检查目录路径是否安全
            return CodeSecurityUtil.isFilePathSafe(workingDirPath);
        } catch (IOException e) {
            return false;
        }
    }

    /**
     * 进程执行结果类
     */
    public static class ProcessResult {
        private boolean success;
        private String output;
        private String error;

        public ProcessResult(boolean success, String output, String error) {
            this.success = success;
            this.output = output;
            this.error = error;
        }

        public boolean isSuccess() {
            return success;
        }

        public String getOutput() {
            return output;
        }

        public String getError() {
            return error;
        }
    }
}

